#ifndef _BLOWFISHIMPL_HXX_
#define _BLOWFISHIMPL_HXX_

#include <openssl/blowfish.h>
#include <string.h>
#include "mydll.h"
#include "mytypes.h"

class BlowfishStreamImpl
{
public:
  BlowfishStreamImpl();

  BlowfishStreamImpl(tByte *buffer, tUInt32 buffersize);

  virtual ~BlowfishStreamImpl();

  virtual void SetKey(const tByte *key, tUInt32 keysize);

  virtual void FinalizeKey();

  /// \brief Returns the start of the buffer.
  virtual tByte *Content();

  /// \brief Set the static buffer (to be decyphered).
  ///
  /// The given buffer of the given length will be used as input 
  /// for all >> operations.
  virtual void SetBuffer(tByte *buffer, tUInt32 buffersize);

  /// \brief Appends the value of the given string into the buffer.
  /// 
  /// The full content of the string will be appended. In order to
  /// delimit two consecutive strings, the '\0' character will be also
  /// appended at the very end of the string so that a string will 
  /// consume its size plus 1. The write pointer is incremented accordingly.
  virtual void Append(const string &text);

  /// \brief Appends the value of the given 64 bits integer into the buffer.
  ///
  /// The integer value will be stored as it is in memory and pushed
  /// into the buffer. The write pointer is incremented by 8 bytes.
  virtual void Append(tUInt64 value);

  /// \brief Appends the value of the given 32 bits integer into the buffer.
  ///
  /// The integer value will be stored as it is in memory and pushed
  /// into the buffer. The write pointer is incremented by 4 bytes.
  virtual void Append(tUInt32 value);

  /// \brief Appends the value of the given 16 bits integer into the buffer.
  ///
  /// The integer value will be stored as it is in memory and pushed
  /// into the buffer. The write pointer is incremented by 2 bytes.
  virtual void Append(tUInt16 value);

  /// \brief Appends the value of the given 8 bits integer into the buffer.
  ///
  /// The integer value will be stored as it is in memory and pushed
  /// into the buffer. The write pointer is incremented by 1 byte.
  virtual void Append(tUInt8 value);

  /// \brief Returns the current read pointer position.
  ///
  /// This function increment the internal read pointer until it encounter 
  /// a NULL ('\0') value.
  ///
  /// CAUTION: This function doesn't guaranty that the returned string
  /// is a real string and that it is NULL ('\0') terminated. It is the 
  /// responsibility of the caller to know what he is doing.
  virtual const char *ScanString();

  /// \brief Extracts a 64 bits integer from the buffer and return it.
  ///
  /// The read pointer is incremented by 8 bytes.
  /// Returns true if succeeded else false if end of buffer has been reached.
  virtual tBool ScanInteger(tUInt64 *value);

  /// \brief Extracts a 32 bits integer from the buffer and return it.
  ///
  /// The read pointer is incremented by 4 bytes.
  /// Returns true if succeeded else false if end of buffer has been reached.
  virtual tBool ScanInteger(tUInt32 *value);

  /// \brief Extracts a 16 bits integer from the buffer and return it.
  ///
  /// The read pointer is incremented by 2 bytes.
  /// Returns true if succeeded else false if end of buffer has been reached.
  virtual tBool ScanInteger(tUInt16 *value);

  /// \brief Extracts a 8 bits integer from the buffer and return it.
  ///
  /// The read pointer is incremented by 1 bytes.
  /// Returns true if succeeded else false if end of buffer has been reached.
  virtual tBool ScanInteger(tUInt8 *value);

  /// \brief Moves both the write and read pointer of the buffer.
  /// 
  /// Next time a new value is pushed in or read from the buffer, 
  /// it will be performed from the new given position.
  virtual void Seek(tUInt32 position);

  /// \brief Empties the buffer content (size becomes 0 after this call).
  virtual void Clear();

  /// \brief Returns the current buffer value.
  virtual tUInt32 Size() const;

  /// \brief Returns the current read index value.
  virtual tUInt32 ReadIndex() const;

  /// \brief Returns the current write index value.
  virtual tUInt32 WriteIndex() const;

  /// \brief Pad eventual uncomplete block and finish encryption/decryption
  /// 
  /// This function should be called before calling Content in case of encryption.
  /// If caller forgot to call this function, then internal buffer may contain
  /// uncomplete encrypted buffer.
  virtual void PadAndFinalize();

private:

  tByte *_keysource;
  BF_KEY *_key;
  tUInt32 _keysize;
  tByte *_buf;
  tUInt32 _bufsize;
  tUInt32 _read_index;
  tUInt32 _write_index;
  tBool _fixed_buf_size;
  mutable tByte *_readcache;
  mutable tUInt32 _readcacheindex;
  tUInt32 _cachedreadblockindex;
  tByte *_writecache;
  tUInt32 _writecacheindex;
  tUInt32 _cachedwriteblockindex;
  mutable tByte *_ivec;
  mutable bool _dirty;

  // Copy constructor is private in order to forbid its usage.
  BlowfishStreamImpl(const BlowfishStreamImpl &model);

  typedef enum
    {
      BFSS_Error=-1,
      BFSS_AlreadyLoaded=0,
      BFSS_SuccessfullyLoaded=1
    } tBFSCacheStatus;

  /// \brief Load into the read cache buffer the requested index block
  ///  containing ciphered data.
  ///
  /// Returns BFSS_Error if block_index is bad (out of allowed bound). If
  /// error is return, the read cache buffer hasn't been changed.
  /// Returns BFSS_AlreadyLoaded if cached buffer already contained the
  /// requested block_index.
  /// Returns BFSS_SuccessfullyLoaded if cache has been updated with
  /// requested block_index.
  tBFSCacheStatus LoadReadCache(tUInt32 block_index) const;

  /// \brief Decrypt Read Cache Buffer and replace ciphered content with
  /// deciphered one.
  void DecryptReadCache(tUInt32 block_index) const;

  /// \brief Encrypt Write Cache Buffer and replace the clear content with
  /// the ciphered one.
  void EncryptWriteCache(tUInt32 block_index);

  bool Grow(tUInt32 newobjectsize);

  inline void PushToWriteCache(const char value)
  {
    _writecache[_writecacheindex]=value;
    _writecacheindex++;

    if (_writecacheindex==8)
      {
	EncryptWriteCache(_write_index);
	memcpy(&_buf[_write_index],&_writecache[0],8);
	_write_index+=8;
	_writecacheindex=0;
	_dirty=false;
      }
    else
      {
	_dirty=true;
      }
  }

  inline void SyncCacheWithBuf(tUInt32 block_index) const
  {
    memcpy(&_buf[block_index*8],&_readcache[0],8);
  }



};


#endif /* _BLOWFISHIMPL_HXX_ */

